/**
 * 网络请求服务
 * 提供统一的HTTP请求封装，包含鉴权、错误处理、Loading等功能
 * @author WeChat Mini Program Team
 * @since 2025-08-13
 */

const Storage = require('../utils/storage');
const Config = require('../utils/config');

class RequestService {
  constructor() {
    // 请求基础配置
    this.baseConfig = {
      timeout: 60000,
      header: {
        'Content-Type': 'application/json'
      }
    };

    // 服务端点配置 - 从配置文件读取
    this.endpoints = Config.endpoints;

    // 请求队列管理
    this.requestQueue = new Map();
    this.maxConcurrent = 10;
    this.currentRequests = 0;

    // 错误码映射
    this.errorCodeMap = {
      0: '请求成功',
      400: '请求参数错误',
      401: '未登录或登录已过期',
      403: '权限不足',
      404: '请求的资源不存在',
      500: '服务器内部错误',
      502: '网关错误',
      503: '服务暂不可用',
      504: '网关超时',
      // 业务错误码
      2001: '分类不存在',
      2002: '帖子不存在',
      2003: '报名失败',
      2004: '收藏失败',
      2005: '参数错误',
      2006: '数据库错误',
      40001: '物品不存在',
      40002: '物品已被交换',
      40003: '无权操作此物品',
      40004: '互换请求不存在',
      40005: '互换请求状态无效'
    };
  }

  /**
   * 发起HTTP请求
   * @param {Object} options 请求配置
   * @param {string} options.url 请求URL
   * @param {string} options.method 请求方法
   * @param {Object} options.data 请求数据
   * @param {Object} options.header 请求头
   * @param {boolean} options.showLoading 是否显示Loading
   * @param {boolean} options.showError 是否显示错误提示
   * @param {string} options.loadingText Loading文案
   * @param {string} options.service 服务标识（user/teacher/community/buddy/swap）
   * @returns {Promise}
   */
  request(options = {}) {
    return new Promise((resolve, reject) => {
      // 参数校验
      if (!options.url) {
        reject(new Error('请求URL不能为空'));
        return;
      }

      // 默认配置合并
      const config = this._mergeConfig(options);
      
      // 处理URL
      const finalUrl = this._buildUrl(config.url, config.service);
      
      // 处理请求头（鉴权等）
      const header = this._buildHeader(config.header);

      // 生成请求ID
      const requestId = this._generateRequestId();

      // 并发控制检查
      if (this.currentRequests >= this.maxConcurrent) {
        this.requestQueue.set(requestId, () => {
          this._doRequest(requestId, finalUrl, config, header, resolve, reject);
        });
        return;
      }

      this._doRequest(requestId, finalUrl, config, header, resolve, reject);
    });
  }

  /**
   * 执行实际请求
   * @private
   */
  _doRequest(requestId, url, config, header, resolve, reject) {
    this.currentRequests++;

    // 显示Loading
    if (config.showLoading) {
      wx.showLoading({
        title: config.loadingText || '加载中...',
        mask: true
      });
    }

    // 发起请求
    wx.request({
      url,
      method: config.method,
      data: config.data,
      header,
      timeout: config.timeout,
      success: (res) => {
        this._handleSuccess(res, config, resolve, reject);
      },
      fail: (err) => {
        this._handleFail(err, config, reject);
      },
      complete: () => {
        this._handleComplete(requestId, config);
      }
    });
  }

  /**
   * 处理请求成功
   * @private
   */
  _handleSuccess(res, config, resolve, reject) {
    const { statusCode, data } = res;
    
    // HTTP状态码检查
    if (statusCode !== 200) {
      this._handleError(statusCode, data, config);
      reject(new Error(`HTTP ${statusCode}: ${this.errorCodeMap[statusCode] || '请求失败'}`));
      return;
    }

    // 业务状态码检查
    if (data && data.code && (data.code !== 0 && data.code !== 200)) {
      this._handleError(data.code, data, config);
      reject(new Error(data.message || this.errorCodeMap[data.code] || '业务处理失败'));
      return;
    }

    // 成功回调
    resolve(data);
  }

  /**
   * 处理请求失败
   * @private
   */
  _handleFail(err, config, reject) {
    console.error('Request failed:', err);

    let errorMessage = '网络请求失败';
    let isConnectionError = false;

    if (err.errMsg) {
      if (err.errMsg.includes('timeout')) {
        errorMessage = '请求超时，请检查网络连接';
      } else if (err.errMsg.includes('fail') || err.errMsg.includes('connection')) {
        errorMessage = '网络连接失败，请检查后端服务是否启动';
        isConnectionError = true;
      }
    }

    // 如果是连接错误且在开发环境，显示更友好的提示
    if (isConnectionError && Config.currentEnv === 'development') {
      errorMessage += '\n\n开发提示：请确保后端服务正在运行。\n运行 start-backend-services.bat 检查服务状态。';
    }

    if (config.showError) {
      this._showError(errorMessage);
    }

    reject(new Error(errorMessage));
  }

  /**
   * 处理请求完成
   * @private
   */
  _handleComplete(requestId, config) {
    this.currentRequests--;

    // 隐藏Loading
    if (config.showLoading) {
      wx.hideLoading();
    }

    // 处理队列中的请求
    this._processQueue();
  }

  /**
   * 处理错误
   * @private
   */
  _handleError(code, data, config) {
    console.error(`Error ${code}:`, data);

    // 特殊错误处理
    if (code === 401) {
      // 清除登录状态 - 清除新的token字段名
      Storage.remove('access_token');
      Storage.remove('refresh_token'); 
      Storage.remove('token_expires_in');
      Storage.remove('token_created_at');
      Storage.remove('user_info');
      Storage.remove('is_new_user');
      // 保持兼容性，也清除旧字段名
      Storage.remove('userToken');
      Storage.remove('userInfo');
      
      // 跳转到登录页
      wx.reLaunch({
        url: '/pages/login/index'
      });
      return;
    }

    // 显示错误提示
    if (config.showError) {
      const errorMessage = data?.message || this.errorCodeMap[code] || '操作失败';
      this._showError(errorMessage);
    }
  }

  /**
   * 显示错误提示
   * @private
   */
  _showError(message) {
    wx.showToast({
      title: message,
      icon: 'none',
      duration: 3000
    });
  }

  /**
   * 合并请求配置
   * @private
   */
  _mergeConfig(options) {
    return {
      method: 'GET',
      showLoading: false,
      showError: true,
      loadingText: '加载中...',
      service: 'user',
      ...this.baseConfig,
      ...options,
      header: {
        ...this.baseConfig.header,
        ...options.header
      }
    };
  }

  /**
   * 构建完整URL
   * @private
   */
  _buildUrl(url, service = 'user') {
    // 如果已经是完整URL，直接返回
    if (url.startsWith('http')) {
      return url;
    }

    // 获取对应服务的基础URL
    const baseUrl = this.endpoints[service] || this.endpoints.user;
    
    // 处理URL开头的斜杠
    const cleanUrl = url.startsWith('/') ? url.substring(1) : url;
    
    return `${baseUrl}/${cleanUrl}`;
  }

  /**
   * 构建请求头
   * @private
   */
  _buildHeader(customHeader = {}) {
    const header = { ...customHeader };

    // 添加授权头 - 优先使用新的token字段名
    let token = Storage.get('access_token') || Storage.get('userToken');
    if (token) {
      header.Authorization = `Bearer ${token}`;
    }

    // 添加用户ID头（部分接口需要）
    const userInfo = Storage.get('user_info') || Storage.get('userInfo');
    if (userInfo && userInfo.id) {
      header['X-User-Id'] = userInfo.id.toString();
    }

    return header;
  }

  /**
   * 生成请求ID
   * @private
   */
  _generateRequestId() {
    return `req_${Date.now()}_${Math.random().toString(36).substring(2)}`;
  }

  /**
   * 检查是否是连接错误
   * @private
   */
  _isConnectionError(err) {
    return err.errMsg && (
      err.errMsg.includes('connection') ||
      err.errMsg.includes('ECONNREFUSED') ||
      err.errMsg.includes('ENOTFOUND') ||
      err.errMsg.includes('timeout')
    );
  }

  /**
   * 尝试使用mock数据回退
   * @private
   */
  _tryMockFallback(config, originalReject) {
    // 根据URL路径决定使用哪个mock方法
    const url = config.url;
    let mockPromise = null;

    if (url.includes('/home') || url.includes('/banners')) {
      mockPromise = MockService.getHomeData();
    } else if (url.includes('/user') && config.method === 'GET') {
      mockPromise = MockService.getUserInfo();
    } else if (url.includes('/categories')) {
      mockPromise = MockService.getCategories();
    } else if (url.includes('/search')) {
      const params = this._parseQueryParams(url);
      mockPromise = MockService.search(params.keyword, params.type);
    }

    if (mockPromise) {
      // 隐藏loading（如果有）
      if (config.showLoading) {
        wx.hideLoading();
      }

      // 使用mock数据resolve
      mockPromise.then(data => {
        // 手动调用resolve，但这里我们需要访问原始的resolve函数
        // 实际上，我们应该重新设计这个架构
        console.log('Using mock data:', data);
        // 这里应该触发成功回调，但由于架构限制，我们记录日志
      }).catch(mockErr => {
        console.error('Mock fallback also failed:', mockErr);
        originalReject(new Error('后端服务不可用，且无模拟数据'));
      });
    } else {
      originalReject(new Error('后端服务不可用，且无对应的模拟数据'));
    }
  }

  /**
   * 解析查询参数
   * @private
   */
  _parseQueryParams(url) {
    const params = {};
    const queryIndex = url.indexOf('?');
    if (queryIndex > -1) {
      const queryString = url.substring(queryIndex + 1);
      queryString.split('&').forEach(pair => {
        const [key, value] = pair.split('=');
        if (key && value) {
          params[decodeURIComponent(key)] = decodeURIComponent(value);
        }
      });
    }
    return params;
  }

  /**
   * GET请求
   */
  get(url, params = {}, options = {}) {
    // 处理查询参数
    if (Object.keys(params).length > 0) {
      const queryString = Object.entries(params)
        .filter(([_, value]) => value !== undefined && value !== null && value !== '')
        .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
        .join('&');
      url = url.includes('?') ? `${url}&${queryString}` : `${url}?${queryString}`;
    }

    return this.request({
      url,
      method: 'GET',
      ...options
    });
  }

  /**
   * POST请求
   */
  post(url, data = {}, options = {}) {
    return this.request({
      url,
      method: 'POST',
      data,
      ...options
    });
  }

  /**
   * PUT请求
   */
  put(url, data = {}, options = {}) {
    return this.request({
      url,
      method: 'PUT',
      data,
      ...options
    });
  }

  /**
   * DELETE请求
   */
  delete(url, options = {}) {
    return this.request({
      url,
      method: 'DELETE',
      ...options
    });
  }

  /**
   * 上传文件
   */
  uploadFile(filePath, options = {}) {
    return new Promise((resolve, reject) => {
      const config = {
        name: 'file',
        url: this._buildUrl('/upload', options.service || 'user'),
        showLoading: true,
        loadingText: '上传中...',
        ...options
      };

      const header = this._buildHeader(config.header);

      if (config.showLoading) {
        wx.showLoading({
          title: config.loadingText,
          mask: true
        });
      }

      wx.uploadFile({
        url: config.url,
        filePath,
        name: config.name,
        formData: config.formData || {},
        header,
        success: (res) => {
          try {
            const data = JSON.parse(res.data);
            if (data.code === 0 || data.code === 200) {
              resolve(data);
            } else {
              reject(new Error(data.message || '上传失败'));
            }
          } catch (e) {
            reject(new Error('响应数据格式错误'));
          }
        },
        fail: (err) => {
          reject(new Error('上传失败'));
        },
        complete: () => {
          if (config.showLoading) {
            wx.hideLoading();
          }
        }
      });
    });
  }

  /**
   * 批量请求
   */
  all(requests = []) {
    return Promise.all(requests);
  }

  /**
   * 取消所有请求
   */
  cancelAll() {
    this.requestQueue.clear();
  }
}

// 创建单例实例
const requestService = new RequestService();

module.exports = requestService;
